package com.example.student_manager_backend.service;

import com.example.student_manager_backend.entity.Course;
import com.example.student_manager_backend.entity.Student;
import com.example.student_manager_backend.exception.BusinessException;
import com.example.student_manager_backend.repository.CourseRepository;
import com.example.student_manager_backend.repository.StudentRepository;
import com.example.student_manager_backend.status.CourseStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * 课程服务类
 * 提供课程相关的业务逻辑处理
 */
@Service
public class CourseService {

    @Autowired
    private CourseRepository courseRepository;

    @Autowired
    private StudentRepository studentRepository; // 注入学生仓储层

    /**
     * 创建课程
     *
     * @param course 课程信息
     * @return 创建的课程
     */
    @Transactional
    public Course createCourse(Course course) {
        if (courseRepository.existsByCourseId(course.getCourseId())) {
            throw new BusinessException("课程编号已存在");
        }
        // 初始化课程状态为 OPEN（可选课）
        course.setStatus(CourseStatus.OPEN);
        return courseRepository.save(course);
    }

    /**
     * 更新课程信息
     *
     * @param courseId 课程编号
     * @param course   课程信息
     * @return 更新后的课程
     */
    @Transactional
    @CacheEvict(value = "courses", key = "#courseId")
    public Course updateCourse(String courseId, Course course) {
        Course existingCourse = courseRepository.findByCourseId(courseId);
        if (existingCourse == null) {
            throw new BusinessException("课程不存在");
        }
        // 禁止修改课程编号
        course.setCourseId(courseId);
        // 保留原有状态，如需允许修改状态可添加逻辑
        course.setStatus(existingCourse.getStatus());
        return courseRepository.save(course);
    }

    /**
     * 删除课程
     *
     * @param courseId 课程编号
     */
    @Transactional
    @CacheEvict(value = "courses", key = "#courseId")
    public void deleteCourse(String courseId) {
        Course course = courseRepository.findByCourseId(courseId);
        if (course == null) {
            throw new BusinessException("课程不存在");
        }
        // 检查课程是否允许删除（如无学生选课）
        if (!course.getStudents().isEmpty()) {
            throw new BusinessException("课程已关联学生，无法删除");
        }
        courseRepository.delete(course);
    }

    /**
     * 根据课程编号查询课程
     *
     * @param courseId 课程编号
     * @return 课程信息
     */
    @Cacheable(value = "courses", key = "#courseId")
    public Optional<Course> findCourseById(String courseId) {
        Course course = courseRepository.findByCourseId(courseId);
        if (course == null) {
            throw new BusinessException("课程不存在");
        }
        return Optional.of(course);
    }

    /**
     * 分页查询课程列表
     *
     * @param spec      查询条件
     * @param pageable  分页参数
     * @return 课程分页列表
     */
    public Page<Course> findCourses(Specification<Course> spec, Pageable pageable) {
        return courseRepository.findAll(spec, pageable);
    }

    /**
     * 根据课程名称模糊查询课程列表
     *
     * @param name 课程名称
     * @return 课程列表
     */
    public List<Course> findCoursesByName(String name) {
        return courseRepository.findByNameContaining(name);
    }

    /**
     * 根据课程类型查询课程列表
     *
     * @param type 课程类型
     * @return 课程列表
     */
    public List<Course> findCoursesByType(String type) {
        return courseRepository.findByType(type);
    }

    /**
     * 学生选课
     *
     * @param courseId 课程ID
     * @param studentId 学生ID
     * @return 更新后的课程信息
     */
    @Transactional
    @CacheEvict(value = "courses", key = "#courseId")
    public Optional<Course> enrollStudent(String courseId, String studentId) {
        // 1. 查询课程与学生
        Course course = findCourseById(courseId).orElseThrow(
                () -> new BusinessException("课程不存在")
        );
        Student student = studentRepository.findById(studentId).orElseThrow(
                () -> new BusinessException("学生不存在")
        );

        // 2. 校验课程状态
        if (course.getStatus() != CourseStatus.OPEN) {
            throw new BusinessException("课程当前不可选（状态：" + course.getStatus().getDescription() + "）");
        }

        // 3. 校验是否已选课
        Set<Student> students = course.getStudents();
        if (students.contains(student)) {
            throw new BusinessException("已选修该课程");
        }

        // 4. 添加双向关联
        students.add(student);
        student.getCourses().add(course);

        // 5. 可选：校验课程容量
        int maxCapacity = course.getMaxCapacity();
        if (students.size() > maxCapacity) {
            students.remove(student); // 回滚操作
            throw new BusinessException("课程人数已满");
        }

        // 6. 保存更新
        courseRepository.save(course);
        return Optional.of(course);
    }

    /**
     * 学生退课
     *
     * @param courseId 课程ID
     * @param studentId 学生ID
     * @return 更新后的课程信息
     */
    @Transactional
    @CacheEvict(value = "courses", key = "#courseId")
    public Optional<Course> dropStudent(String courseId, String studentId) {
        Course course = findCourseById(courseId).orElseThrow(
                () -> new BusinessException("课程不存在")
        );
        Student student = studentRepository.findById(studentId).orElseThrow(
                () -> new BusinessException("学生不存在")
        );

        // 校验课程状态（允许退课的状态）
        if (!CourseStatus.OPEN.equals(course.getStatus()) && !CourseStatus.CLOSED.equals(course.getStatus())) {
            throw new BusinessException("课程已结束，无法退课");
        }

        Set<Student> students = course.getStudents();
        if (!students.remove(student)) {
            throw new BusinessException("未选修该课程，无法退课");
        }
        student.getCourses().remove(course); // 双向移除

        courseRepository.save(course);
        return Optional.of(course);
    }
}